package com.demo.tx.seata.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.demo.miya.controller.client.SeataMiyaClient;
import com.demo.tx.seata.dao.mapper.UserMapper;
import com.demo.tx.seata.model.entity.User;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 测试seata分布式事务
 * @author cyj
 */
@Slf4j
@Service
public class SeataService {

    @Resource
    private SeataMiyaClient seataMiyaClient;

    @Resource
    private UserMapper userMapper;

    /**
     * 测试分布式数据插入
     * @param userId 用户id
     */
    @GlobalTransactional(rollbackFor = Exception.class)
    public void insert(Long userId) {
        User user = new User().setUserId(userId);
        userMapper.insert(user);
    }

    /**
     * 测试分布式事务嵌套
     * <br>
     * AT模式：<br>
     * 这个场景比较特殊，首先在user表插入数据，然后再根据user_id更新数据。此时，更新数据失败，user表数据被清空，
     * undo_log表插入user表的操作记录被删除，但是undo_log表更新user表的操作记录被保留。
     * 事务回滚流程：更新失败后，seata会发起回滚，回滚操作是按照事务分支创建先后顺序执行。
     * 首先回滚插入操作，把user表数据删除和undo_log表数据删除。然后再回滚更新操作，回滚更新操作时发现user表记录已经不存在，所以无法回滚。
     * seata会将undo_log表数据保留，提示需要手动删除
     * @param userId 用户id
     */
    @GlobalTransactional(rollbackFor = Exception.class)
    public void embeddedTx(Long userId) {
        User user = new User().setUserId(userId);
        userMapper.insert(user);

        updateError(userId);
    }

    /**
     * 更新抛异常
     * @param userId 用户id
     */
    @GlobalTransactional(rollbackFor = Exception.class)
    public void updateError(Long userId) {
        User user = new User().setUserId(userId).setUserName("userName");
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(User::getUserId, user.getUserId());
        userMapper.update(user, updateWrapper);
        throw new RuntimeException("抛异常");
    }

    /**
     * 更新用户名称，sleepTime模拟处理业务，一直获取seata事务全局锁，不释放
     * @param userId 用户id
     * @param sleepTime 休眠时间，单位秒
     */
    @GlobalTransactional(rollbackFor = Exception.class)
    public void updateUser(Long userId, Long sleepTime) throws InterruptedException {
        User user = new User().setUserId(userId).setUserName("userName" + sleepTime);
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(User::getUserId, user.getUserId());
        userMapper.update(user, updateWrapper);

        TimeUnit.SECONDS.sleep(sleepTime);
    }

    /**
     * 调用第三方服务，一个服务数据插入成功，一个服务报异常
     * 依赖service-miya服务
     * @param userId 用户id
     */
    @GlobalTransactional(rollbackFor = Exception.class)
    public void insertThirdServiceError(Long userId) {
        User user = new User().setUserId(userId);
        userMapper.insert(user);

        seataMiyaClient.insert(userId);

        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserId, userId);
        log.info("userId = {}， users = {}", userId, JSON.toJSONString(userMapper.selectList(queryWrapper)));
        seataMiyaClient.insertError(userId);
    }
}
